package com.longshine.mobile.component.util.soapclient;

import java.io.InputStream;
import java.io.StringBufferInputStream;
import java.net.URI;

import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;

import com.longshine.mobile.component.util.HttpConnection;
import com.longshine.mobile.component.util.soapclient.WSDLParser.OperationEntry;

public class SOAPConnection {

	private WSDLParser wsdlParser;
	private String lastElementName;

	private OperationEntry operationEntry;
	private static boolean finished;
	private static boolean hasError;

	private static StringBuffer resultString;
	private static StringBuffer faultCode;
	private static StringBuffer faultString;

	static {
		resultString = new StringBuffer();
		faultCode = new StringBuffer();
		faultString = new StringBuffer();
	}

	public SOAPConnection(String wsdlUrl) {
		this(WSDLParser.loadWithUrl(wsdlUrl));
	}

	public SOAPConnection(WSDLParser wsdlParser) {
		this.wsdlParser = wsdlParser;
	}

	public String getTargetNamespace(String serviceName) {
		return wsdlParser.getServiceEntry(serviceName).targetNamespace;
	}

	public String getServiceLocation(String serviceName, String portName) {
		return wsdlParser.getServiceEntry(serviceName).getPort(portName).serviceLocation;
	}

	public String getActionName(String serviceName, String portName,
			String operationName) {
		return getTargetNamespace(serviceName) + "/" + operationName;
	}

	public OperationEntry getOperationEntry(String serviceName,
			String portName, String operationName) {
		return wsdlParser.getServiceEntry(serviceName).getPort(portName)
				.getOperation(operationName);
	}

	public String getRequestOperationMessageContext(
			OperationEntry _operationEntry, Object[] params,
			String targetNamespace) {

		StringBuffer sb = new StringBuffer();
		sb.append("<" + _operationEntry.name + " xmlns=\"" + targetNamespace
				+ "\">\n");

		for (int i = 0; i < _operationEntry.inputMessage.parts.length; i++) {
			String partName = _operationEntry.inputMessage.parts[i].toString();
			sb.append("<" + partName + ">" + params[i] + "</" + partName
					+ ">\n");
		}

		sb.append("</" + _operationEntry.name + ">\n");

		return sb.toString();
	}

	public String getRequestSOAPMessage(OperationEntry _operationEntry,
			Object[] params, String targetNamespace) {
		StringBuffer sb = new StringBuffer();
		sb.append("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n");
		sb.append("<soap:Envelope xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" "
				+ "xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" "
				+ "xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\">\n");
		sb.append("<soap:Body>\n");
		sb.append(getRequestOperationMessageContext(_operationEntry, params,
				targetNamespace));
		sb.append("</soap:Body>\n");
		sb.append("</soap:Envelope>");
		return sb.toString();
	}

	public String invoke(String serviceName, String portName,
			String operationName, Object[] params) throws Exception {

		finished = false;

		String uri = getServiceLocation(serviceName, portName);

		if (uri == null) {
			return null;
		}

		String targetNamespace = getTargetNamespace(serviceName);

		operationEntry = getOperationEntry(serviceName, portName, operationName);

		String soapMessage = getRequestSOAPMessage(operationEntry, params,
				targetNamespace);

		String actionName = getActionName(serviceName, portName, operationName);

		HttpPost httpPost = new HttpPost(new URI(uri));
		httpPost.setHeader("Content-Type", "text/xml; charset=utf-8");
		httpPost.setHeader("SOAPAction", actionName);

		StringEntity se = new StringEntity(soapMessage, "UTF-8");// 设置body(这里是soap格式的xml)
		httpPost.setEntity(se);

		byte[] receivedData = null;

		try {
			receivedData = HttpConnection.httpConnect(httpPost);
		} catch (Exception e) {
			e.printStackTrace();
		}

		if (receivedData != null) {
			try {
				InputStream is = new StringBufferInputStream(new String(
						receivedData));

				saxParser(is);
			} catch (Exception e) {
				e.printStackTrace();
			}
		}

		if (hasError) {
			throw new Exception();
		}

		return resultString.length() > 0 ? resultString.toString() : null;
	}

	public void saxParser(InputStream is) throws Exception {
		SAXParserFactory factory = SAXParserFactory.newInstance();
		SAXParser parser = factory.newSAXParser();
		SaxParserHandler handler = new SaxParserHandler();
		parser.parse(is, handler);
	}

	public class SaxParserHandler extends DefaultHandler {

		@Override
		public void startElement(String uri, String localName, String qName,
				Attributes attributes) throws SAXException {
			super.startElement(uri, localName, qName, attributes);
			lastElementName = localName;
		}

		@Override
		public void characters(char[] ch, int start, int length)
				throws SAXException {
			super.characters(ch, start, length);
			if ((operationEntry.outputMessage.parts[0]).equals(lastElementName)) {
				resultString.append(ch, start, length);
			} else {
				if ("faultcode".equals(lastElementName)) {
					hasError = true;
					faultCode.append(ch);
				} else if ("faultstring".equals(lastElementName)) {
					hasError = true;
					faultString.append(ch);
				}
			}
		}

		@Override
		public void endElement(String uri, String localName, String qName)
				throws SAXException {
			super.endElement(uri, localName, qName);
		}
	}
}